package com.thd.springboot.framework.cloud.feign.configuration;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.thd.springboot.framework.cloud.feign.log.FeignLoggingListener;
import com.thd.springboot.framework.cloud.feign.log.InfoFeignLogger;
import feign.Client;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.optionals.OptionalDecoder;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Enumeration;

/**
 * feign接口配置
 *
 * @author LiLong
 * @date: 2019/04/02 16:52
 */
@SpringBootConfiguration
@EnableFeignClients
@ConditionalOnClass({Client.class, ObjectMapper.class})
public class FeignAutoConfiguration {
    
    private org.slf4j.Logger logger = LoggerFactory.getLogger(FeignAutoConfiguration.class);
    @Bean
    Logger.Level feignLevel() {
        /**
         * Logger.Level有如下几种选择：
         * NONE, 不记录日志 (默认)。
         * BASIC, 只记录请求方法和URL以及响应状态代码和执行时间。
         * HEADERS, 记录请求和应答的头的基本信息。
         * FULL, 记录请求和响应的头信息，正文和元数据。
         */
        return Logger.Level.FULL;
    }

    @Bean
    @ConditionalOnBean(FeignLoggingListener.class)
    Logger infoFeignLogger() {
        return new InfoFeignLogger();
    }

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    @Bean
    public Decoder feignDecoder() {
        return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter())));
    }

    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(getMappingJackson2HttpMessageConverter());
        return () -> httpMessageConverters;
    }

    private MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(new ObjectMapper() {{
            setSerializationInclusion(JsonInclude.Include.NON_NULL);
            // setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//            registerModule(new SimpleModule().addDeserializer(Date.class, new DateDeserializer()));
            JavaTimeModule timeModule = new JavaTimeModule();
            registerModule(timeModule);
            setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        }});
        return converter;
    }


    /**
     * 请求拦截(Feign发送请求前)
     * @return
     */
    @Bean
    @ConditionalOnBean(RequestInterceptor.class)
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            System.out.println(" FeignConfiguration : =================================");
            ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attrs != null) {
                HttpServletRequest request = attrs.getRequest();
                // 如果在Cookie内通过如下方式取
                Cookie[] cookies = request.getCookies();

                if (cookies != null && cookies.length > 0) {
                    for (Cookie cookie : cookies) {
                        logger.info(" restTemplate set cookie :" + cookie.getName() + "," + cookie.getValue());
                        requestTemplate.header(cookie.getName(), cookie.getValue());
                    }
                } else {
                    logger.warn("FeignHeadConfiguration", "获取Cookie失败！");
                }
                // 如果放在header内通过如下方式取
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        String value = request.getHeader(name);
                        /**
                         * 遍历请求头里面的属性字段，将jsessionid添加到新的请求头中转发到下游服务
                         * */
                        logger.info(" restTemplate set head :" + name + "," + value);
                        if ("jsessionid".equalsIgnoreCase(name)) {
                            logger.debug("添加自定义请求头key:" + name + ",value:" + value);
                            requestTemplate.header(name, value);
                        } else {
                            logger.debug("FeignHeadConfiguration", "非自定义请求头key:" + name + ",value:" + value + "不需要添加!");
                        }

                        if ("cookie".equalsIgnoreCase(name)) {
                            logger.debug("添加自定义请求头key:" + name + ",value:" + value);
                            requestTemplate.header(name, value);
                        } else {
                            logger.debug("FeignHeadConfiguration", "非自定义请求头key:" + name + ",value:" + value + "不需要添加!");
                        }
                    }
                } else {
                    logger.warn("FeignHeadConfiguration", "获取请求头失败！");
                }
            }
        };
    }
}
